package world.snowcrystal.aa;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.log4j.Log4j2;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseCookie;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.util.StringUtils;
import world.snowcrystal.properties.AuthenticationProperties;
import world.snowcrystal.utils.JwtUtils;

import java.io.IOException;
import java.util.Enumeration;

/**
 * 这个类在认证成功后被调用，这个类将检查是否需要提供 Remember Me 服务；并选择设置 Token 到 UA
 * 之后，将 forward 到一个指定 URL；这个类参考了 {@link org.springframework.security.web.authentication.ForwardAuthenticationSuccessHandler}
 *
 * @author tianqing
 * @since 1.0
 */
@Log4j2
public class RememberMeAuthenticationSuccessHandler
        implements AuthenticationSuccessHandler {


    private AuthenticationProperties properties;


    public RememberMeAuthenticationSuccessHandler(AuthenticationProperties properties) {
        this.properties = properties;
    }


    /**
     * Called when a user has been successfully authenticated.
     *
     * @param request        the request which caused the successful authentication
     * @param response       the response
     * @param authentication the <tt>Authentication</tt> object which was created during
     *                       the authentication process.
     */
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request,
                                        HttpServletResponse response,
                                        Authentication authentication)
            throws IOException, ServletException {


        if (!requireRememberMeService(request)) {
            return;
        } else {
            // 显然促使登录成功的原因有两种：表单认证成功，或者 token 认证成功
            // 如果是表单认证的，那么 request 上一定有  username 与 password 命名的 parameter
            // 如果是从 token 认证的，那么  request 上一定有 username 与 password 命名的 attribute,
            // 第二点由 SUsernamePasswordAuthenticationFilter 保证
            // 这里的 Authentication 实例来自于数据库，所以密码直接是加密的
            // 但是这里需要的是明文
            String username = request.getParameter("username");
            String password = request.getParameter("password");
            String fingerprint = request.getParameter("fingerprint");

            if (!StringUtils.hasText(username) || !StringUtils.hasText(password)) {
                username = request.getAttribute("username").toString();
                password = request.getAttribute("password").toString();
            }
            request.setAttribute("username", username);
            request.setAttribute("password", password);
            // 或许会有其他内容需要保存到 Cookie 中
            // 但是现在只需要保存 username,password
            addRememberMeCookie(username, password, response);
            addFingerprintCookie(fingerprint, response);
        }
    }

    public void addFingerprintCookie(String fingerprint,HttpServletResponse response) {
        addCookie("fingerprint",fingerprint,response);
    }



    /**
     * 在响应中设置 jwt cookie
     *
     * @param username 用户名，必须是明文
     * @param password 密码，必须是明文
     * @author tianqing
     * @since 1.0
     */
    public void addRememberMeCookie(String username, String password,
                           HttpServletResponse response) {
        String token = JwtUtils.makeJWT(username, password);
        addCookie(properties.getRememberMeCookieName(), token, response);
    }

    private void addCookie(String name,String value,HttpServletResponse response){
        ResponseCookie cookie = ResponseCookie.from(name, value)
                .sameSite("None")
                .maxAge(properties.getCookieMaxAge())
                .domain(properties.getCookieDomain())
                .httpOnly(properties.getCookieHttpOnly())
                .secure(properties.getCookieSecure())
                .path("/")
                .build();
        if(response.isCommitted()){
            log.error("The response has been committed.");
        }
        response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
    }

    private boolean requireRememberMeService(HttpServletRequest request) {
        Enumeration<String> parameterNames = request.getParameterNames();
        String parameterName = properties.getRememberMeParameterName();
        while (parameterNames.hasMoreElements()) {
            String p = parameterNames.nextElement();
            if (p.equals(parameterName)) {
                String value = request.getParameter(p);
                return !"0".equals(value) && !"false".equals(value);
            }
        }
        return false;
    }


}
